﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using log4net;
using LumenWorks.Framework.IO.Csv;
using Snap.Model;

namespace MarketData
{
    public class TradingPhysicsAdapter
    {

        private static readonly ILog Logger =
            LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private const string TickIndicators = "MarketIndicator.Buy.Executed.VolumeA+" +
                                              "MarketIndicator.Sell.Executed.VolumeA+" +
                                              "MarketIndicator.Buy.Executed.PriceAvg+" +
                                              "MarketIndicator.Sell.Executed.PriceAvg";

        private const string GetDataQuery =
            "getdata?type={0}&date={1}&stock={2}&format={3}&compression={4}&tick={5}&indicators={6}";
        private const string GetTicketQuery = "getticket?C={0}&P={1}?{2}";

        private readonly string customerNumber;
        private readonly string passwordHash;
        private readonly string quoteServerUrl;
        private string ticket;
        private readonly string tempDirectory;
        private Stopwatch stopWatch;

        public TradingPhysicsAdapter(string customerNumber, string passwordHash, string quoteServerUrl)
        {
            this.customerNumber = customerNumber;
            this.passwordHash = passwordHash;
            this.quoteServerUrl = quoteServerUrl;

            stopWatch = new Stopwatch();

            // Clean directory where file will be stored
            tempDirectory = Path.Combine(Path.GetTempPath(), "Snap");
            if(Directory.Exists( tempDirectory )) Directory.Delete(tempDirectory, true);
        	Directory.CreateDirectory( tempDirectory );
        }

        public IEnumerable<Ticker> GetTickIndicators( string symbol, DateTime date, int tickLengthMilliseconds )
        {
            string dataQuery = String.Format( GetDataQuery,
                                              DataTypes.Indicators,
                                              date.Date.ToString("yyyyMMdd"),
                                              symbol,
                                              DataFormat.Csv,
                                              CompressionType.None,
                                              tickLengthMilliseconds,
                                              TickIndicators );
            Logger.DebugFormat( "Using dataQuery: [{0}]", dataQuery );

            // Get ticket to authorize the data retrieval using the above query
            string ticketQuery = String.Format( GetTicketQuery,
                                                customerNumber,
                                                passwordHash,
                                                dataQuery );
            Logger.DebugFormat( "Using ticketQuery: [{0}]", ticketQuery );

            var tickers = new List<Ticker>();
            ticket = GetTicket( ticketQuery );
            if (String.IsNullOrEmpty( ticket ))
            {
                // No pull possible. Get out.
                return tickers;
            }

            dataQuery += ("&t=" + ticket);
            // Download
            var fileInfo = DownloadMarketData( dataQuery );
            if (!fileInfo.Exists)
            {
                // File is not there. Get out.
                return tickers;
            }

            using (var csvReader = new CsvReader(new StreamReader(fileInfo.FullName), true))
            {
                while (csvReader.ReadNextRecord())
                {
                    string dateAsString = String.Format( "{0} {1}", date.ToString( "yyyy-MM-dd" ), csvReader[0] );
                    tickers.Add( new Ticker
                                     {
                                         Tick = DateTime.Parse( dateAsString ).Ticks,
                                         BuyVolume = Int32.Parse( csvReader[1] ),
                                         SellVolume = Int32.Parse( csvReader[2] ),
                                         BuyPriceAvg = Decimal.Parse( csvReader[3] ),
                                         SellPriceAvg = Decimal.Parse( csvReader[4] ),
                                         CompanySymbol = symbol,
                                         MarketDate = date.Date
                                     }
                        );
                }
            }

            return tickers;
        }

        /// <summary>
        /// Gets the ticket.
        /// </summary>
        /// <param name="ticketQuery">The ticket query.</param>
        /// <returns></returns>
        private string GetTicket( string ticketQuery )
        {
            string returnTicket = String.Empty;

            // Request Ticket
            var request = WebRequest.Create( quoteServerUrl + ticketQuery );
            try                                     
            {
                var response = (HttpWebResponse)request.GetResponse();
                if (null == response)
                {
                    throw new Exception( "Null response" );
                }
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    throw new Exception( "HTTP Error fetching ticket: " + response.StatusDescription );
                }

                // Get ticket from response stream if transaction was successful
                using (var reader = new StreamReader(response.GetResponseStream()  ))
                {
                    returnTicket = reader.ReadToEnd();
                }
            }
            catch (WebException we)
            {
                // Bad request
                Logger.ErrorFormat( "Web Exception fetching Ticket: {0}", we.Message );
                // Read error description from response stream
                if (we.Response != null)
                {
                    using (var reader = new StreamReader(we.Response.GetResponseStream()))
                    {
                        Logger.ErrorFormat( "Description: {0}", reader.ReadToEnd() );
                    }
                }
            }
            catch(Exception e)
            {
                Logger.ErrorFormat( "Exception fetching Ticket: {0}", e.Message );
            }

            Logger.DebugFormat( "Got ticket: [{0}]", returnTicket );
            return (returnTicket);
        }

        /// <summary>
        /// Gets the data.
        /// </summary>
        /// <param name="dataQuery">The info of the temp file.</param>
        private FileInfo DownloadMarketData(string dataQuery)
        {
            var fileInfo = new FileInfo(Path.Combine(tempDirectory, Guid.NewGuid() + ".csv"));
            var request = WebRequest.Create( quoteServerUrl + dataQuery );
            try
            {
                var response = (HttpWebResponse)request.GetResponse();
                if (null == response)
                {
                    const string message = "NULL response from HttpWebResponse getting data.";
                    Logger.Error( message );
                    throw new Exception( message );
                }
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    Logger.ErrorFormat( "HTTP Error getting data: {0}", response.StatusCode );
                }

                // Get the data
                stopWatch.Restart();
                using (var reader = new StreamReader( response.GetResponseStream() ))
                using (var writer = new StreamWriter( fileInfo.FullName ))
                {
                    while (!reader.EndOfStream)
                    {
                        writer.WriteLine( reader.ReadLine() );
                    }
                }
                stopWatch.Stop();
                Logger.DebugFormat( "Downloaded market data file (size {0}kb) in {1}",
                                    fileInfo.Length / 1000.0,
                                    stopWatch.Elapsed );
            }
            catch (WebException we)
            {
                // bad request
                Logger.ErrorFormat( "Web Exception getting data: {0}", we.Message );
                // read error description from response stream
                if (we.Response != null)
                {
                    using (var reader = new StreamReader( we.Response.GetResponseStream() ))
                    {
                        Logger.ErrorFormat( "Description: {0}", reader.ReadToEnd() );
                    }
                }
            }
            catch (Exception e)
            {
                Logger.ErrorFormat("Exception getting data: {0}", e.Message);
            }

            return (fileInfo);
        }
    }
}
